#pragma once

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <string>
#include <list>
#include <unordered_map>
#include <exception>
#include <functional>
#include <memory>
#include <utility>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <ctime>
#include <cstdint>
#include <easyfs.h>

#if defined (EASYLOG_C_SYTLE_FORMAT)
#   include <cstdarg>
#   if defined (__linux__) || defined (__APPLE__)
#      ifndef _GNU_SOURCE
#      define _GNU_SOURCE
#      endif
#   endif
#elif defined (EASYLOG_CPP20_STD_FORMAT)
#   include <format>
#else
#   define FMT_HEADER_ONLY
#   include <fmt/core.h>
#endif

#if defined (__APPLE__) || defined (__linux__)
#   include <execinfo.h>
#   include "signal.h"
#elif defined (WIN32) || defined (WIN64)
#   include <dbghelp.h>
#   include <Windows.h>
#   pragma comment(lib, "DbgHelp.lib")
#endif

namespace easylog
{
    struct except : std::exception
    {
        except(const std::string& s)
            : _s(s)
        {}

        virtual const char* what() const throw()
        {
            return _s.c_str();
        }

    private:
        std::string _s;
    };
}

/*
 * 实用工具类
 *
 * - 时间工具：获取时间\格式化时间
 * - 颜色工具：加颜色\消颜色
 * - 文件工具：获取大小\创建删除\读写\路径解析
 * - 代码定位：获取文件名\行号\列号\函数名
 * - 格式化器：c和c++式的字符串格式化
 *
 **/

namespace easylog
{
namespace util
{
    struct time
    {
        enum time_enum
        {
            t1sec   =  1,            t2sec   =  2,            t3sec   =  3,
            t4sec   =  4,            t5sec   =  5,            t6sec   =  6,
            t7sec   =  7,            t8sec   =  8,            t9sec   =  9,
            t10sec  = 10,            t15sec  = 15,            t20sec  = 20,
            t1min   =  1 * 60,       t2min   =  2 * 60,       t3min   =  3 * 60,
            t4min   =  4 * 60,       t5min   =  5 * 60,       t6min   =  6 * 60,
            t7min   =  7 * 60,       t8min   =  8 * 60,       t9min   =  9 * 60,
            t10min  = 10 * 60,       t15min  = 15 * 60,       t20min  = 20 * 60,
            t1hour  =  1 * 60 * 60,  t2hour  =  2 * 60 * 60,  t3hour  =  3 * 60 * 60,
            t4hour  =  4 * 60 * 60,  t5hour  =  5 * 60 * 60,  t6hour  =  6 * 60 * 60,
            t7hour  =  7 * 60 * 60,  t8hour  =  8 * 60 * 60,  t9hour  =  9 * 60 * 60,
            t10hour = 10 * 60 * 60,  t15hour = 15 * 60 * 60,  t20hour = 20 * 60 * 60,
        };

        static time_t now()
        {
            return std::time(nullptr);
        }

        static std::string format(time_t time, const std::string& fmt)
        {
            struct tm t;
#if defined (_WIN32) || defined (_WIN64)
            localtime_s(&t, &time);
#else
            localtime_r(&time, &t);
#endif
            char buf[32] = {0};
            strftime(buf, 32, fmt.c_str(), &t);

            return buf;
        }
    };

    struct color
    {
        enum color_enum
        {
            none = 0,
            black,
            light_black,
            red,
            light_red,
            green,
            light_green,
            yellow,
            light_yellow,
            blue,
            light_blue,
            purple,
            light_purple,
            cyan,
            light_cyan,
            white,
            light_white,
        };

        static const char* onclr(color_enum cl)
        {
            // Must use int not colors here, because clang will not convert colors to integer,
            // so there is no hash method for colors.
            static std::unordered_map<int, const char*> cols = {
                {         none, "\033[0m"    }, // none
                {        black, "\033[0;30m" }, // black
                {  light_black, "\033[1;30m" }, // dark_gray
                {          red, "\033[0;31m" }, // red
                {    light_red, "\033[1;31m" }, // light_red
                {        green, "\033[0;32m" }, // green
                {  light_green, "\033[1;32m" }, // light_green
                {       yellow, "\033[0;33m" }, // brown
                { light_yellow, "\033[1;33m" }, // yellow
                {         blue, "\033[0;34m" }, // blue
                {   light_blue, "\033[1;34m" }, // light_blue
                {       purple, "\033[0;35m" }, // purple
                { light_purple, "\033[1;35m" }, // light_purple
                {         cyan, "\033[0;36m" }, // cyan
                {   light_cyan, "\033[1;36m" }, // light_cyan
                {        white, "\033[0;37m" }, // light_gray
                {  light_white, "\033[1;37m" }, // white
            };

            if (cols.find(cl) == cols.end())
                throw except("color: unknown color");

            return cols[cl];
        }

        static const char* offclr()
        {
            return "\033[0m";
        }
    };

    struct file
    {
        static size_t size(const std::string path)
        {
            return easyfs::file_size(path);
        }

        static void mkdir(const std::string& path)
        {
            easyfs::create_directory(path);
        }

        static void mkfile(const std::string& path)
        {
            easyfs::create_file(path);
        }

        static void remove(const std::string& path)
        {
            easyfs::remove(path);
        }

        static std::string read(const std::string& path)
        {
            return easyfs::read_file(path);
        }

        static void write(std::ofstream& ofs, const std::string& data)
        {
            ofs.write(data.c_str(), data.size());
        }

        static bool exists(const std::string& path)
        {
            return easyfs::exists(path);
        }

        static std::string dirpath(const std::string& path)
        {
            return easyfs::path(path).parent_path().string();
        }
    };

    class source_loc
    {
    public:
        source_loc(const char* file = __builtin_FILE(),
                   const char* func = __builtin_FUNCTION(),
                   const int   line = __builtin_LINE(),
#if defined (__APPLE__) || defined (_WIN32) || defined (_WIN64)
                   const int   colu = __builtin_COLUMN())
#else // gcc can't get column number
                   const uint32_t colu = 0)
#endif
            : file(file), function(func), line(line), column(colu)
        {}

        const char* file;
        const char* function;
        int line;
        int column;
    };

    struct payload
    {
#if defined (EASYLOG_C_SYTLE_FORMAT)
        static std::string format(const char* fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);

            char* res = nullptr;
            if (vasprintf(&res, fmt, ap) == -1)
            {
                va_end(ap);
                throw except("vasprintf error");
            }
            va_end(ap);

            std::unique_ptr<char> auto_raii(res); // raii will automatic free 'res'
            return res;
        }
#else
        template<typename... Args>
        static std::string format(const char* fmt, Args&&... args)
        {
    #if defined (EASYLOG_STD_FORMAT)
            return std::vformat(fmt, std::make_format_args(args...));
    #else
            return fmt::format(fmt, std::forward<Args>(args)...);
    #endif
        }
#endif

#if defined (_WIN32) || defined (_WIN64)
        static int vasprintf(char** strp, const char* fmt, va_list ap)
        {
            int len = _vscprintf(fmt, ap);
            if (len == -1)
                return -1;

            char* res = (char*)malloc(len + 1);
            if (res == nullptr)
                return -1;
            memset(res, 0, len + 1);

            int n = vsnprintf(res, len + 1, fmt, ap);
            if (n == -1)
            {
                free(res);
                return -1;
            }

            *strp = res;
            return n;
        }
#endif
    };

    struct size
    {
        enum size_enum
        {
            m1KB   = 1024,
            m5KB   = m1KB   * 5,
            m10KB  = m1KB   * 10,
            m20KB  = m1KB   * 20,
            m50KB  = m10KB  * 5,
            m1MB   = m1KB   * 1024,
            m5MB   = m1MB   * 5,
            m10MB  = m1MB   * 10,
            m20MB  = m1MB   * 20,
            m50MB  = m10MB  * 5,
            m100MB = m10MB  * 10,
            m200MB = m100MB * 2,
            m300MB = m100MB * 3,
            m400MB = m100MB * 4,
            m500MB = m100MB * 5,
            m600MB = m100MB * 6,
            m700MB = m100MB * 7,
            m800MB = m100MB * 8,
            m900MB = m100MB * 9,
            m1GB   = m500MB * 2,
        };

        static size_t tomsgnum(size_t size)
        {
            return size / 128; // the length of is approximately 128 bytes
        }
    };

    
    struct tracer
    {
        static std::string stacktrace()
#if defined (__APPLE__) || defined (__linux__)
        {

            std::unique_ptr<void*[]> trace(new void*[32]{0});

            int len = backtrace(trace.get(), 32);
            char** msgs = backtrace_symbols(trace.get(), len);

            std::string res;

            for (int i = 0; i < len; i++)
            {
                 res += msgs[i];
            }

            free(msgs);
            return res;
        }
#elif defined (_WIN32) || defined (_WIN64)
        {
            HANDLE proc = GetCurrentProcess();
            SymInitialize(proc, NULL, TRUE);

            void* stack[100] = { nullptr };
            unsigned short num = CaptureStackBackTrace(0, 100, stack, NULL);

            std::unique_ptr<SYMBOL_INFO> symbol(new SYMBOL_INFO[sizeof(SYMBOL_INFO) + 256 * sizeof(char)]);
            symbol->MaxNameLen = 255;
            symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

            std::stringstream ss;

            for (int i = 0; i < num; i++)
            {
                SymFromAddr(proc, (DWORD64)(stack[i]), 0, symbol.get());
                ss << i << ": " << symbol->Name << '\n';
            }

            return ss.str();
        }
#endif
    };

    struct killer
    {
#if defined (__APPLE__) || defined (__linux__)
        static void kill_self()
        {
            raise(9);
        }
#elif defined (WIN32) || defined (WIN64)
        static void kill_self()
        {
            HANDLE handle = GetCurrentProcess();
            TerminateProcess(handle, 0);
            CloseHandle(handle);
        }
#endif
    };

}
    using color_enum = util::color::color_enum;
    using time_enum = util::time::time_enum;
    using size_enum = util::size::size_enum;
}


/*
 * 日志等级类
 *
 * 1. 定义日志等级枚举变量
 * 2. 定义日志等级变量转字符串的接口
 *
 **/


namespace easylog
{
    struct level
    {
        enum lv_enum
        {
            on = -1,
            debug,
            info,
            warn,
            error,
            fatal,
            off,
        };

        static std::string level2str(lv_enum lv)
        {
            switch (lv)
            {
            case debug: return "DEBUG";
            case info:  return "INFO";
            case warn:  return "WARN";
            case error: return "ERROR";
            case fatal: return "FATAL";
            case off:   return "OFF";
            default: throw except("easylog::level2str: illegal level");
            }
        }

        static lv_enum str2level(const std::string& s)
        {
            if      (s == "DEBUG") return debug;
            else if (s == "INFO")  return info;
            else if (s == "WARN")  return warn;
            else if (s == "ERROR") return error;
            else if (s == "FATAL") return fatal;
            else if (s == "OFF")   return off;
            else throw except("easylog::str2level: illegal level");
        }
    };
}

/*
 * 日志消息类
 *
 * 综合日志消息需要的各项要素：
 * 1. 产生时间
 * 2. 日志等级
 * 3. 源文件名
 * 4. 代码行号
 * 5. 线程名称
 * 6. 日志器名
 * 7. 消息载荷
 *
 **/

namespace easylog
{
    struct message
    {
        time_t          time;
        level::lv_enum  levo;
        std::string     file;
        size_t          line;
        std::string     func;
        std::thread::id tid;
        std::string     lname;
        std::string     payload;

        message(level::lv_enum lv, const char* fl, size_t ln, const char* fc, const char* name, std::string pload)
            : time(util::time::now())
            , levo(lv)
            , file(fl)
            , line(ln)
            , func(fc)
            , tid(std::this_thread::get_id())
            , lname(name)
            , payload(pload)
        {}

        // This constructor is only provided for the parser of filename pattern
        // in file sinker or rolling sinker.
        message(level::lv_enum lv = level::on)
            : time(util::time::now())
            , levo(lv)
            , file(__FILE__)
            , line(__LINE__)
            , func(__FUNCTION__)
            , tid(std::this_thread::get_id())
        {}

        // for queue::vector<message> constructor
        message(int) {}
    };
} 

/*
 * 配置信息类
 *
 * 以单例模式存储配置信息
 *
 **/


namespace easylog
{
#if defined (__APPLE__)
        using color_mode = std::unordered_map<int, color_enum>; // clang won't convert level and colors to int
#else
        using color_mode = std::unordered_map<level::lv_enum, color_enum>;
#endif

    class config
    {
    public:
        static config& instance()
        {
            static config inst;
            return inst;
        }

        level::lv_enum get_level()            { return _level;          }
        std::string    get_pattern()          { return _pattern;        }
        bool           get_oncolor()          { return _colorful;        }
        color_mode     get_color()            { return _colorset;       }
        std::string    get_fn_pattern()       { return _fn_patt;        }
        size_t         get_max_file_size()    { return _max_file_size;  }
        size_t         get_max_live_time()    { return _max_live_time;  }
        size_t         get_max_history()      { return _max_history;    }
        size_t         get_total_size_cap()   { return _total_size_cap; }
        bool           get_work_type()        { return _work_type;      }
        size_t         get_thread_num()       { return _thread_num;     }
        size_t         get_buffer_size()      { return _buffer_size;    }
        size_t         get_buffer_incr()      { return _buffer_incr;    }
        size_t         get_buffer_thres()     { return _buffer_thres;   }

        void set_level(level::lv_enum lv)            { _level = lv;            }
        void set_pattern(const std::string& p)       { _pattern = p;           }
        void set_colorful(bool color)                { _colorful = color;      }
        void set_color(const color_mode& cols)       { _colorset = cols;       }
        void set_fn_pattern(const std::string& patt) { _fn_patt = patt;        }
        void set_max_file_size(size_enum size)       { _max_file_size = size;  }
        void set_max_live_time(time_enum time)       { _max_live_time = time;  }
        void set_max_history(size_t num)             { _max_history = num;     }
        void set_total_size_cap(size_enum size)      { _total_size_cap = size; }
        void set_work_type(bool type)                { _work_type = type;      }
        void set_thread_num(size_t num)              { _thread_num = num;      }
        void set_buffer_size(size_t size)            { _buffer_size = size;    }
        void set_buffer_incr(size_t incr)            { _buffer_incr = incr;    }
        void set_buffer_thres(size_t thre)           { _buffer_thres = thre;   }

    private:
        config()
            : _level(default_level)
            , _pattern(default_pattern)
            , _colorful(default_colorful)
            , _colorset(default_colorset)
            , _fn_patt(default_fn_pattern)
            , _max_history(default_max_history)
            , _max_live_time(default_max_live_time)
            , _max_file_size(default_max_file_size)
            , _total_size_cap(default_total_size_cap)
            , _work_type(default_work_type)
            , _thread_num(default_thread_num)
            , _buffer_size(default_buffer_size)
            , _buffer_incr(default_buffer_incrment)
            , _buffer_thres(default_buffer_threshold)
        {}

        config(const config&) = delete;
        config& operator=(const config&) = delete;

        level::lv_enum default_level    = level::debug;
        std::string    default_pattern  = "[%d{%F %H:%M:%S}][%p][%t][%c][%f:%l]%T%m%T[%a]%n";
        bool           default_colorful = false;
        color_mode     default_colorset =
        {
            { level::debug, color_enum::none         },
            { level::info,  color_enum::green        },
            { level::warn,  color_enum::yellow       },
            { level::error, color_enum::light_purple },
            { level::fatal, color_enum::red          },
        };
        std::string  default_fn_pattern     = "log-%d{%Y%m%d-%H%M%S}.log";
        size_t       default_max_history    = 10;
        time_enum    default_max_live_time  = time_enum::t2sec;
        size_enum    default_max_file_size  = size_enum::m1MB;
        size_enum    default_total_size_cap = size_enum::m300MB;

        bool   default_work_type            = false;
        size_t default_thread_num           = 1;
        size_t default_buffer_size          = size_enum::m10KB;
        size_t default_buffer_incrment      = size_enum::m1KB;
        size_t default_buffer_threshold     = size_enum::m20KB;

    private:
        level::lv_enum _level;
        std::string _pattern;

        bool _colorful;
        color_mode _colorset;

        std::string _fn_patt;
        size_t _max_history;
        time_enum _max_live_time;
        size_enum _max_file_size;
        size_enum _total_size_cap;

        bool _work_type;
        size_t _thread_num;
        size_t _buffer_size;
        size_t _buffer_incr;
        size_t _buffer_thres;
    };
}
/*
 * 格式化器类
 *
 * 使用格式化子项保存各个子项的格式化方法，
 * 然后交给格式化器按规则串指定的顺序格式化出日志消息字符串。
 *
 **/

namespace easylog
{
    struct format_item
    {
        using ptr = std::shared_ptr<format_item>;
        virtual void format(std::ostream& out, const message& msg) = 0;
    };

    struct time_format_item : public format_item
    {
        time_format_item(const std::string& fmt) : _fmt(fmt)
        {}

        virtual void format(std::ostream& out, const message& msg) override
        {
            out << util::time::format(msg.time, _fmt);
        }

        std::string _fmt;
    };

    // used to format tabs
    struct tab_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << '\t';
        }
    };

    // used to format thread id
    struct tid_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            // Output tid in hex, then convert back to decimal.
            out << std::hex << std::showbase << msg.tid << std::dec;
        }
    };

    struct level_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << level::level2str(msg.levo);
        }
    };

    // used to format logger name
    struct lname_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << msg.lname;
        }
    };

    struct file_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << msg.file;
        }
    };

    struct line_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << msg.line;
        }
    };

    struct func_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << msg.func;
        }
    };

    struct pload_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << msg.payload;
        }
    };

    // used to format line feed
    struct lfeed_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << '\n';
        }
    };

    // used to format percentages.
    struct hcent_format_item : public format_item
    {
        virtual void format(std::ostream& out, const message& msg) override
        {
            out << '%';
        }
    };

    // format item for serial number, used in the toggles of toggle file name.
    struct order_format_item : public format_item
    {
        order_format_item(const std::string& n) : _n(n)
        {}

        virtual void format(std::ostream& out, const message& msg) override
        {
            out << _n;
        }

        std::string _n;
    };

    struct other_format_item : public format_item
    {
        other_format_item(const std::string& other) : _other(other)
        {}

        virtual void format(std::ostream& out, const message& msg) override
        {
            out << _other;
        }

        std::string _other;
    };

    class formatter
    {
    public:
        using ptr = std::shared_ptr<formatter>;

    public:
        formatter(const std::string& pattern = config::instance().get_pattern())
            : _pattern(pattern)
        {
            parser();
        }

        // This constructor is only provided for the formatting of toggle file's filename.
        // The filename pattern and the value of order need to be provided.
        formatter(const std::string& pattern, size_t num)
            : _pattern(pattern)
        {
            parser(num);
        }

        // external interface for formatting message object.
        std::string format(message&& msg)
        {
            std::stringstream ss;

            for (auto& item : _items)
                item->format(ss, std::forward<message>(msg));

            return ss.str();
        }

        void set_pattern(const std::string& pattern)
        {
            _pattern = pattern;
            parser();
        }

        formatter(const formatter&) = default;
        formatter(formatter&&) = default;

        formatter& operator=(const formatter&) = default;
        formatter& operator=(formatter&&) = default;

    private:
        // Define format control characters.
        enum specifier
        {
            time  = 'd',
            tab   = 'T',
            tid   = 't',
            level = 'p',
            lname = 'c',
            file  = 'f',
            line  = 'l',
            func  = 'a',
            pload = 'm',
            lfeed = 'n',
            hcent = '%',
            order = 'i',
            other =  -1,
        };

        // [%d{%F %H:%M:%S}][%p][%t][%c][%f:%l]%T%m%n
        //
        // Parse the formatted rule string provided by users,
        // and append the correspond format items into the items array.
        bool parser(size_t num = 0)
        {
            _items.clear();

            size_t i = 0;
            while (i < _pattern.size())
            {
                if (_pattern[i] == '%') // Format item encountered.
                {
                    ++i;
                    if (_pattern.size() > i) // Ensure there are characters after %.
                    {
                        if (_pattern[i] == time) // Parse time format item.
                        {
                            size_t b = i + 1;
                            size_t e = b + 1;

                            if (_pattern.size() > b && _pattern[b] == '{')
                            {
                                while (e < _pattern.size() && _pattern[e] != '}')
                                    ++e;

                                if (e >= _pattern.size() || _pattern[e] != '}')
                                    throw except("easylog::formatter::paser: time parser error, no }");

                                append_item(time, _pattern.substr(b + 1, e - b - 1));
                                i = e + 1;
                            }
                            else
                            {
                                throw except("timme parser error, no {");
                            }
                        }
                        else if (_pattern[i] == order) // File name pattern of toggling file can also be considered as a pattern,
                                                       // and can also be handed for parsing.
                                                       // Therefore, %i is used only for format file name of toggling files.
                        {
                            append_item(_pattern[i], std::to_string(num)); // %i represents the order, which is param num.
                            ++i;
                        }
                        else if (isctrlkey(_pattern[i])) // Apart from time and order , others will process here.
                        {
                            append_item(_pattern[i]);
                            ++i;
                        }
                        else
                        {
                            throw except("no format control character behind %");
                        }
                    }
                    else throw except("parser error, nothing behind %");
                }
                else // ordinary characters encountered, not specifier
                {
                    append_item(other, std::string(1, _pattern[i]));
                    ++i;
                }
            }
            return true;
        }

        // Insert items correspond to specifier into the format item array.
        void append_item(const char key, const std::string& val = "")
        {
            if      (key == time)  _items.push_back(std::make_shared<time_format_item>(val));
            else if (key == tab)   _items.push_back(std::make_shared<tab_format_item>());
            else if (key == tid)   _items.push_back(std::make_shared<tid_format_item>());
            else if (key == level) _items.push_back(std::make_shared<level_format_item>());
            else if (key == lname) _items.push_back(std::make_shared<lname_format_item>());
            else if (key == file)  _items.push_back(std::make_shared<file_format_item>());
            else if (key == line)  _items.push_back(std::make_shared<line_format_item>());
            else if (key == func)  _items.push_back(std::make_shared<func_format_item>());
            else if (key == pload) _items.push_back(std::make_shared<pload_format_item>());
            else if (key == lfeed) _items.push_back(std::make_shared<lfeed_format_item>());
            else if (key == hcent) _items.push_back(std::make_shared<hcent_format_item>());
            else if (key == order) _items.push_back(std::make_shared<order_format_item>(val));
            else if (key == other) _items.push_back(std::make_shared<other_format_item>(val));
            else throw except("formatter::append_item: unexpected key");
        }

        bool isctrlkey(const char key)
        {
            return key == time
                || key == tab
                || key == tid
                || key == level
                || key == lname
                || key == file
                || key == line
                || key == func
                || key == pload
                || key == lfeed
                || key == hcent
                || key == order;
        }

    private:
        std::string _pattern;
        std::vector<format_item::ptr> _items;
    };

}
/*
 * 日志落地模块
 *
 * 将格式化后的日志字符串输出到指定位置，
 * 支持标准输出、文件、滚动文件，支持拓展落地方向
 * 使用工厂模式管理所有落地类的创建
 *
 **/


namespace easylog
{
    class sinker
    {
    public:
        using ptr = std::shared_ptr<sinker>;

    public:
        sinker(formatter::ptr fmtr = std::make_shared<formatter>())
            : _fmtr(fmtr)
        {}

        void set_pattern(const std::string& pattern)
        {
            _fmtr->set_pattern(pattern);
        }

        void set_formatter(formatter::ptr fmtr)
        {
            _fmtr = fmtr;
        }

        // interface all sinkers used
        virtual ~sinker() {}
        virtual void sink(message&& msg) = 0;

        // interfaces only used by stdout sinker
        virtual void set_color(bool onclr) = 0;
        virtual void set_color_mode(color_mode mode) = 0;

        virtual void set_logfile(const std::string& path) = 0;
        virtual void set_fn_patt(const std::string& patt) = 0;

        virtual void set_order(size_t n) = 0;
        virtual void set_max_history(size_t n) = 0;
        virtual void set_max_file_size(size_enum size) = 0;
        virtual void set_max_live_time(time_enum time) = 0;
        virtual void set_total_size_cap(size_enum size) = 0;

    protected:
        // Create the logfile and the directory,
        // and use the parameters ofs to reopen it.
        void open_logfile(std::ofstream* ofs, std::string path)
        {
            util::file::mkdir(util::file::dirpath(path));
            util::file::mkfile(path);

            if (ofs == nullptr)
                throw except("sinker: ofstream pointer is null");

            ofs->close(); // Close it in advance, otherwise open will throw an exception.
            ofs->open(path, std::ios::out | std::ios::app);
        }

    protected:
        formatter::ptr _fmtr;
    };

    class stdout_sinker : public sinker
    {
    public:
        stdout_sinker(bool colorful = config::instance().get_oncolor(),
                color_mode colmode  = config::instance().get_color(),
            formatter::ptr fmtr     = std::make_shared<formatter>())
            : _colorful(colorful)
            , _colmode(colmode)
            , sinker(fmtr)
        {}

        virtual void set_color(bool onclr) override { _colorful = onclr; }

        virtual void set_color_mode(color_mode mode) override { _colmode = mode; }

        virtual void sink(message&& msg) override
        {
            if (_colorful)
            {
                std::cout << util::color::onclr(_colmode[msg.levo])
                          << _fmtr->format(std::forward<message>(msg))
                          << util::color::offclr();
            }
            else
            {
                std::cout << _fmtr->format(std::forward<message>(msg));
            }
        }

        // useless
        virtual void set_logfile(const std::string& path) override
        {
            throw except("call set_logfile by stdout_sinker is not allow");
        }
        virtual void set_fn_patt(const std::string& patt) override
        {
            throw except("call set_fs_patt by stdout_sinker is not allow");
        }
        virtual void set_order(size_t n) override
        {
            throw except("call set_order by stdout_sinker is not allow");
        }
        virtual void set_max_file_size(size_enum size) override
        {
            throw except("call set_max_file_size by stdout_sinker is not allow");
        }
        virtual void set_max_live_time(time_enum time) override
        {
            throw except("call set_max_live_time by stdout_sinker is not allow");
        }
        virtual void set_max_history(size_t n) override
        {
            throw except("call set_max_history by stdout_sinker is not allow");
        }
        virtual void set_total_size_cap(size_enum size) override
        {
            throw except("call set_total_size_cap by stdout_sinker is not allow");
        }

    private:
        bool _colorful;
        color_mode _colmode;
    };

    class file_sinker : public sinker
    {
    public:
        file_sinker(const std::string& patt = config::instance().get_fn_pattern(),
                        formatter::ptr fmtr = std::make_shared<formatter>())
            : sinker(fmtr)
        {
            set_fn_patt(patt);
        }

        virtual void set_logfile(const std::string& path) override
        {
            // Delete old abandoned logfile that may exist
            if (!_fname.empty() && util::file::exists(_fname))
                util::file::remove(_fname);

            // open new logfile
            _fname = path;
            open_logfile(&_ofs, _fname);
        }

        virtual void set_fn_patt(const std::string& patt) override
        {
            const auto& fname = formatter(patt).format(message()); // filename pattern is handed over to formatter,
                                                                   // to generate the actual filename.
            set_logfile(fname);
        }

        // Format the message and output it to log file.
        virtual void sink(message&& msg) override
        {
            std::string data = _fmtr->format(std::forward<message>(msg));
            util::file::write(_ofs, data);
        }

        // useless
        virtual void set_color(bool onclr) override
        {
            throw except("call set_color by file_sinker is not allow");
        }
        virtual void set_color_mode(color_mode mode) override
        {
            throw except("call set_color_mode by file_sinker is not allow");
        }
        virtual void set_order(size_t n) override
        {
            throw except("call set_order by file_sinker is not allow");
        }
        virtual void set_max_history(size_t n) override
        {
            throw except("call set_max_history by file_sinker is not allow");
        }
        virtual void set_max_file_size(size_enum size) override
        {
            throw except("call set_max_file_size by file_sinker is not allow");
        }
        virtual void set_max_live_time(time_enum time) override
        {
            throw except("call set_max_live_time by file_sinker is not allow");
        }
        virtual void set_total_size_cap(size_enum size) override
        {
            throw except("call set_total_size_cap by file_sinker is not allow");
        }

    protected:
        std::string _fname;   // actual filename
        std::ofstream _ofs;
    };

    // rolling is controlled by tow parameters,
    // max_file_size limits the size of each log file,
    // max_live_time limits the usage time of each log file,
    //
    // In addition, total_size_cap and max_history are not used to controll rolling,
    // only to decide whether to delete old files.
    class rolling_sinker : public sinker
    {
    public:
        rolling_sinker(const std::string& fn_patt   = config::instance().get_fn_pattern(),
                                   size_t history   = config::instance().get_max_history(),
                                   size_t file_size = config::instance().get_max_file_size(),
                                   size_t live_time = config::instance().get_max_live_time(),
                                   size_t size_cap  = config::instance().get_total_size_cap(),
                           formatter::ptr fmter     = std::make_shared<formatter>())
            : sinker(fmter)
            , _fname_patt(fn_patt)
            , _order(1)
            , _total_size(0)
            , _max_history(history)
            , _max_fsize(file_size)
            , _max_ltime(live_time)
            , _total_size_cap(size_cap)
        {}

        // The setting of filename pattern will take effect since the next rolling.
        virtual void set_fn_patt(const std::string& patt) override { _fname_patt = patt; }

        virtual void set_order(size_t n) override { _order = n; }

        virtual void set_max_history(size_t n) override { _max_history = n; }

        virtual void set_total_size_cap(size_enum size) override { _total_size_cap = size; }

        virtual void set_max_live_time(time_enum time) override { _max_ltime = time; }

        virtual void set_max_file_size(size_enum size) override { _max_fsize = size; }

    protected:
        virtual void roll() = 0;

        void toggle_logfile()
        {
            // Lock the toggle process to ensure thread safety.
            // (ps: it isn't necessary,
            //      because the entire output process is thread-safe with logger locking.)
            std::unique_lock<std::mutex> lock(_mtx);

            // open a new logfile
            std::string fname = formatter(_fname_patt, _order++).format(message());
            _history_files.push_back(fname);

            open_logfile(&_ofs, fname);

            // no need to strictly check whether to delete old logfiles each sink,
            // it is sufficient to delete them when rolling.
            //
            // If there are too many log files or the total size is too large,
            // remove the oldest file.
            if (_history_files.size() > _max_history || _total_size >= _total_size_cap)
            {
                _total_size -= util::file::size(_history_files.front());

                util::file::remove(_history_files.front());
                _history_files.pop_front();
            }
        }

        // useless
        virtual void set_color(bool onclr) override
        {
            throw except("call set_color by rolling_sinker is not allow");
        }
        virtual void set_color_mode(color_mode mode) override
        {
            throw except("call set_color_mode by rolling_sinker is not allow");
        }
        virtual void set_logfile(const std::string& path) override
        {
            throw except("call set_logfile by rolling_sinker is not allow");
        }

    protected:
        std::string _fname_patt;
        std::ofstream _ofs;

        size_t _order;      // rolling file's sequence number record
        size_t _total_size; // current total size of all the log file

        size_t _max_fsize;      // limits the size of each log file
        size_t _max_ltime;      // limits the usage time of each log file,
        size_t _max_history;    // limits the count of log file
        size_t _total_size_cap; // limits the total size of all the log file

        std::list<std::string> _history_files; // history file record
        std::mutex _mtx; // lock when toggling logfile
    };

    class roll_over_time : public rolling_sinker
    {
    public:
        roll_over_time(const std::string& pattern   = config::instance().get_fn_pattern(),
                                   size_t history   = config::instance().get_max_history(),
                                   size_t file_size = config::instance().get_max_file_size(),
                                   size_t live_time = config::instance().get_max_live_time(),
                                   size_t size_cap  = config::instance().get_total_size_cap(),
                           formatter::ptr fmter     = std::make_shared<formatter>())
            : rolling_sinker(pattern, history, file_size, live_time, size_cap, fmter)
            , _cur_time(0) // Initialize cur_time to 0,
                           // ensuring the rolling function must be called to open the rolling file,
                           // before the first time sink.
        {}

        roll_over_time(size_t live_time)
            : rolling_sinker(config::instance().get_fn_pattern(),
                             config::instance().get_max_history(),
                             config::instance().get_max_file_size(),
                             live_time,
                             config::instance().get_total_size_cap())
            , _cur_time(0)
        {}

        // The usage time of log file is too long or
        // the total size of all log files is too large
        // Both will result in the log file being rolled over.
        virtual void roll() override
        {
            if (_cur_time + _max_ltime <= util::time::now())
            {
                toggle_logfile();
                _cur_time = util::time::now();
            }
        }

        // Before each time sink is rolling check.
        // If satisfied, we will roll the logfile then continue the output.
        virtual void sink(message&& msg) override
        {
            roll();

            std::string data = _fmtr->format(std::forward<message>(msg));
            util::file::write(_ofs, data);

            // Update the total size in a timely manner.
            _total_size += data.size();
        }

        // useless
        virtual void set_max_file_size(size_enum size) override
        {
            throw except("call set_max_file_size by roll_over_time is not allow");
        }

    private:
        time_t _cur_time;
    };

    class roll_over_size : public rolling_sinker
    {
    public:
        roll_over_size(const std::string& pattern   = config::instance().get_fn_pattern(),
                                   size_t history   = config::instance().get_max_history(),
                                   size_t file_size = config::instance().get_max_file_size(),
                                   size_t live_time = config::instance().get_max_live_time(),
                                   size_t size_cap  = config::instance().get_total_size_cap(),
                           formatter::ptr fmter     = std::make_shared<formatter>())
            : rolling_sinker(pattern, history, file_size, live_time, size_cap, fmter)
            , _cur_size(file_size) // Initialize cur_size to max_size,
                                   // ensuring the rolling function must be called to open the rolling file,
                                   // before the first time sink.
        {}

        roll_over_size(size_t file_size)
            : rolling_sinker(config::instance().get_fn_pattern(),
                             config::instance().get_max_history(),
                             file_size,
                             config::instance().get_max_live_time(),
                             config::instance().get_total_size_cap())
            , _cur_size(file_size)
        {}

        virtual void roll() override
        {
            if (_cur_size >= _max_fsize)
            {
                toggle_logfile();
                _cur_size = 0;
            }
        }

        virtual void sink(message&& msg) override
        {
            roll();

            std::string data = _fmtr->format(std::forward<message>(msg));
            util::file::write(_ofs, data);

            _cur_size += data.size();
            _total_size += data.size();
        }

        // useless
        virtual void set_max_live_time(time_enum time) override
        {
            throw except("call set_max_live_time by roll_over_size is not allow");
        }

    private:
        size_t _cur_size;
    };

    // Use factory mode to agent the creation of sinkers.
    struct sinker_factory
    {
        template<typename SinkType, typename... Args>
        static sinker::ptr create(Args&&... args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}
/*
 * 缓冲区模块
 *
 * 定义一个队列缓冲区，扩容、尾插和头取。
 *
 **/


namespace easylog
{
    template<class value>
    class ring_queue
    {
    public:
        using self = ring_queue<value>;

    public:
        ring_queue(size_t size, size_t incr, size_t thres)
            : _buf(size + 1)
            , _cap(size)
            , _increment(incr)
            , _threshold(thres)
            , _rear(0)
            , _front(0)
        {}

        void set_size(size_t cap) { _cap = cap; }

        void set_incrment(size_t incr) { _increment = incr; }

        void set_threshold(size_t thres) { _threshold = thres; }

        void push(value&& in)
        {
            _buf[_rear++] = std::forward<value>(in);
            _rear %= _cap + 1;
        }

        void pop(value& out)
        {
            out = std::move(_buf[_front++]);
            _front %= _cap + 1;
        }

        size_t size()
        {
            return (_rear -_front + _cap + 1) % (_cap + 1);
        }

        bool wrable()
        {
            return !((_rear + 1) % (_cap + 1) == _front);
        }

        bool rdable()
        {
            return !(_rear == _front);
        }

        void reserve()
        {
            size_t incr = 0;
            if (_cap < _threshold) incr = _cap * 2;
            else incr = _cap + _increment;

            _cap += incr;
            _buf.resize(_buf.size() + incr);
        }

        void swap(self& q)
        {
            _buf.swap(q._buf);
            std::swap(_cap, q._cap);
            std::swap(_increment, q._increment);
            std::swap(_threshold, q._threshold);
            std::swap(_rear, q._rear);
            std::swap(_front, q._front);
        }

    private:
        std::vector<value> _buf;
        size_t _cap;
        size_t _rear;
        size_t _front;
        size_t _increment;  // increment of expandsion
        size_t _threshold;  // threshold of expandsion
        // Before reaching the threshold, expand by twice,
        // after that, expand by the incremental size.
    };
}
/*
 * 异步工作器
 *
 * 包含异步线程，生产消费缓冲区，锁和条件变量维护线程安全。
 * 异步日志器将日志交给异步工作器，并提供日志的处理接口。
 *
 **/


namespace easylog
{
    template <class buffer, class value>
    class async_worker
    {
    public:
        using ptr     = std::shared_ptr<async_worker>;
        using handler = std::function<void(buffer&)>;

        enum work_type
        {
            safe = false,
            unsafe = true,
        };

    public:
        async_worker(work_type type, size_t thnum, size_t qsize, size_t incr, size_t thres, const handler& handle)
            : _pdr_queue(qsize, incr, thres)
            , _csr_queue(qsize, incr, thres)
            , _type(type)
            , _handler(handle)
            , _stop(false)
        {
            run(thnum);
        }

        void run(size_t num)
        {
            while (num--)
            {
                _th_pool.emplace_back(&async_worker::routine, this);
            }
        }

        void set_thread_num(size_t num)
        {
            _th_pool.resize(num);
        }

        void set_work_type(work_type type) { _type = type; }

        void set_buffer_size(size_t cap)
        {
            _pdr_queue.set_size(cap);
            _csr_queue.set_size(cap);
        }

        void set_handler(handler handl) { _handler = handl; }

        ~async_worker() { stop(); }

        void stop()
        {
            _stop = true;
            _csr_cv.notify_all();

            for (auto& th : _th_pool)
                th.join();
        }

        void push(value&& msg)
        {
            std::unique_lock<std::mutex> lock(_mtx);

            // When buffer is full, producer will be block in safe mode,
            // until the buffer is writable
            if (_type == safe)
            {
                _pdr_cv.wait(lock, [&]{ return _pdr_queue.wrable(); });
            }
            else if (!_pdr_queue.wrable())
            {
                _pdr_queue.reserve();
            }

            // push data and notify consumers
            _pdr_queue.push(std::forward<value>(msg));
            _csr_cv.notify_all();
        }

        // consumer working routine
        // _handler is data processing method provided by the aync logger.
        void routine()
        {
            // Even if stop is true, thread should be terminated after clearing the buffer.
            while (!_stop || _csr_queue.rdable() || _pdr_queue.rdable())
            {
                do // buffer checking and swapping.
                {
                    std::unique_lock<std::mutex> lock(_mtx);

                    if (_csr_queue.rdable()) // if consumer buffer is readable, jump to handle that
                        break;

                    // otherwise, if consumer buffer is empty,
                    // consumer wait until producer buffer is readable,
                    // then swap the two buffers and consumer continue to work.
                    _csr_cv.wait(lock, [&]{ return _stop || _pdr_queue.rdable(); });
                    _pdr_queue.swap(_csr_queue);

                    // if in safe mode, notify producer continue to work,
                    // because producer buffer is empty now.
                    if (_type == safe)
                        _pdr_cv.notify_all();
                }
                while(0);

                _handler(_csr_queue); // process one message
            }
        }

    private:
        buffer _pdr_queue;
        buffer _csr_queue;
        work_type _type;
        std::mutex _mtx;
        std::condition_variable _pdr_cv;
        std::condition_variable _csr_cv;
        std::vector<std::thread> _th_pool;
        std::atomic<bool> _stop;
        handler _handler;
    };
}
/*
 * 日志器模块
 *
 * 通过日志器调用落地器输出日志，
 * 通过日志器建造器使用建造者模式创建日志器。
 * 通过日志器管理器管理所有的日志器。
 *
 * 两种日志器分别是同步日志器和异步日志器，
 * 同步日志器直接输出日志消息，异步日志器将日志消息对象交给异步工作器进行输出。
 *
 **/


namespace easylog
{
    class logger
    {
    public:
        struct fmt_with_loc;

        using ptr        = std::shared_ptr<logger>;
        using source_loc = util::source_loc;
        using fmt_str_t  = fmt_with_loc;
        using sinkers_t  = std::vector<sinker::ptr>;

        // put source_location and format_string as class members,
        // make it easy to define output api param.
        struct fmt_with_loc
        {
            fmt_with_loc(const char* s, source_loc l = source_loc()) : fmt(s), loc(l) {}
            const char* fmt;
            source_loc loc;
        };

    public:
        logger(const std::string& name, level::lv_enum level, const sinkers_t& sinkers)
            : _name(name), _level(level), _sinkers(sinkers)
        {}

        virtual ~logger() {}

        const std::string& get_name() { return _name; }

        level::lv_enum get_level() { return _level; }

        void set_level(level::lv_enum lv) { _level = lv; }

        void set_name(const std::string& name) { _name = name; }

        const sinkers_t& get_sinkers() { return _sinkers; }

        void set_sinkers(const sinkers_t& sinkers) { _sinkers = sinkers; }

        template<typename... Args> void debug(fmt_str_t fmt, Args&&... args) { dolog(fmt.loc, level::debug, fmt.fmt, std::forward<Args>(args)...); }
        template<typename... Args> void info (fmt_str_t fmt, Args&&... args) { dolog(fmt.loc, level::info , fmt.fmt, std::forward<Args>(args)...); }
        template<typename... Args> void warn (fmt_str_t fmt, Args&&... args) { dolog(fmt.loc, level::warn , fmt.fmt, std::forward<Args>(args)...); }
        template<typename... Args> void error(fmt_str_t fmt, Args&&... args) { dolog(fmt.loc, level::error, fmt.fmt, std::forward<Args>(args)...); }
        template<typename... Args> void fatal(fmt_str_t fmt, Args&&... args) 
        {
            dolog(fmt.loc, level::fatal, fmt.fmt, std::forward<Args>(args)...);

#if not defined (EASYLOG_PRINT_STACK_TRACE_WHEN_FATAL)
            for (auto& sinker : _sinkers)
                sinker->set_pattern("[stacktrace] %m%n");

            dolog(fmt.loc, level::fatal, util::tracer::stacktrace().c_str());
            util::killer::kill_self();
#endif
        }

    protected:
        template<typename... Args>
        void dolog(const source_loc& loc, level::lv_enum lv, const char* fmt, Args&&... args)
        {
            if (lv < _level)
                return;

            log(message(lv, loc.file, loc.line, loc.function, _name.c_str(), \
                        util::payload::format(fmt, args...)));
        }

        virtual void log(message&& msg) = 0;

    protected:
        std::string _name;
        level::lv_enum _level;
        sinkers_t _sinkers;
        std::mutex _mtx;
    };

    // synchronous logger
    // without any buffering, directly outputs logs to sinker
    class sync_logger : public logger
    {
    public:
        sync_logger(const std::string& name, level::lv_enum level, sinkers_t& sinkers)
            : logger(name, level, sinkers)
        {}

        virtual void log(message&& msg) override
        {
            std::unique_lock<std::mutex> lock(_mtx);

            for (auto& sinker : _sinkers)
            {
                sinker->sink(message(msg));
                // Can not use perfect forward here, which will transfer the memory of msg,
                // and the next sinker will not be able to obtain msg.
            }
        }
    };

    class async_logger : public logger
    {
    public:
        using value     = message;
        using buffer    = ring_queue<value>;
        using worker    = async_worker<buffer, value>;
        using work_type = worker::work_type;

    public:
        async_logger(const std::string& name,
                         level::lv_enum level,
                             sinkers_t& sinkers,
                              work_type type,
                                 size_t thnum,
                                 size_t qsize,
                                 size_t incr,
                                 size_t thres)
            : logger(name, level, sinkers)
            , _worker(new worker(type, thnum, qsize, incr, thres, \
                        std::bind(&async_logger::handler, this, std::placeholders::_1)))
        {}

        virtual void log(message&& msg) override
        {
            // push operator in worker is thread safed, no need to lock
            _worker->push(std::forward<message>(msg));
        }

        void handler(buffer& buf)
        {
            value msg(0);
            // {
                std::unique_lock<std::mutex> lock(_mtx);
                if (buf.rdable())
                    buf.pop(msg);
                else
                    return;
            // }
            // {
                // std::unique_lock<std::mutex> lock(_hanle_mtx); // Use _handle_mtx instead of _mtx,
                                                               // cause the pc_model must decouple data consumption and data processing,
                                                               // to improve efficiency
                for (auto& sinker : _sinkers)
                    sinker->sink(value(msg));
            // }
        }

        void set_thread_num(size_t num)
        {
            _worker->set_thread_num(num);
        }

    private:
        worker::ptr _worker;
        std::mutex _hanle_mtx;
    };

    // Use the Builder pattern to construct logger.
    // It is divided into local logger builder and global logger builder.
    // The global builder will put logger in registry.
    class logger_builder
    {
    public:
        using ptr = std::shared_ptr<logger_builder>;
        using work_type = async_logger::work_type;

        enum logger_type
        {
            sync,
            async,
        };

    public:
        logger_builder() {}

        void build_logger_type(logger_type type) { _ltype = type; }

        void build_logger_name(const std::string& name) { _lname = name; }

        void build_limit_level(level::lv_enum lv = config::instance().get_level()) { _level = lv; }

        template<typename SinkType, typename... Args>
        void build_sinker(Args&&... args)
        {
            _sinkers.push_back(sinker_factory::create<SinkType>(std::forward<Args>(args)...));
        }

        void build_work_type(work_type type = \
                config::instance().get_work_type() ? \
                    work_type::unsafe : work_type::safe)
        {
            _wtype = type;
        }

        void build_thread_num(size_t num = config::instance().get_thread_num())
        {
            _thnum = num;
        }

        void build_buffer_size(size_t size = config::instance().get_buffer_size())
        {
            _qsize = util::size::tomsgnum(size);
        }

        void build_buffer_increment(size_t incre = config::instance().get_buffer_incr())
        {
            _incre = util::size::tomsgnum(incre);
        }

        void build_buffer_threshold(size_t thres = config::instance().get_buffer_thres())
        {
            _thres = util::size::tomsgnum(thres);
        }

        virtual logger::ptr build() = 0;

    protected:
        logger::ptr create()
        {
            if (_lname.empty())
            {
                throw except("build logger failed cause name is empty");
            }
            if (_sinkers.empty())
            {
                _sinkers.push_back(std::make_shared<stdout_sinker>());
            }
            if (_level == level::on)
            {
                throw except("build logger failed cause illegal level");
            }

            if (_ltype == sync)
            {
                return std::make_shared<sync_logger>(_lname, _level, _sinkers);
            }
            else if (_ltype == async)
            {
                return std::make_shared<async_logger>(_lname, _level, _sinkers, _wtype, _thnum, _qsize, _incre, _thres);
            }
            else
            {
                throw except("build logger need invoke build_logger_type function");
            }
        }

    protected:
        logger_type _ltype;
        std::string _lname;
        level::lv_enum _level;
        logger::sinkers_t _sinkers;

        work_type _wtype;
        size_t _thnum;
        size_t _qsize;
        size_t _incre;
        size_t _thres;
    };


    class local_logger_builder : public logger_builder
    {
    public:
        virtual logger::ptr build() override
        {
            return create();
        }
    };


    // registry is used to manage all loggers,
    // it provides interfaces for popping and inserting loggers,
    // protected by locks, making it thread-safe.
    class registry
    {
    public:
        static registry& instance()
        {
            static registry rsg;
            return rsg;
        }

        void add(const logger::ptr& lgr)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _registry.emplace(lgr->get_name(), lgr);
        }

        bool has(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            return _registry.find(name) != _registry.end();
        }

        logger::ptr get(const std::string& name)
        {
            if (!has(name))
                throw except("registry::get_logger: unknown logger");
            return _registry[name];
        }

        logger::ptr default_logger()
        {
            return _registry["default"];
        }

    private:
        registry()
        {
            build_default_logger();
        }

        void build_default_logger()
        {
            logger_builder::ptr llb = std::make_shared<local_logger_builder>();
            llb->build_logger_name("default");
            llb->build_logger_type(logger_builder::logger_type::sync);
            llb->build_limit_level(config::instance().get_level());
            llb->build_sinker<stdout_sinker>();
            add(llb->build());
        }

        registry(const registry&) = delete;
        registry& operator=(const registry&) = delete;

    private:
        std::unordered_map<std::string, logger::ptr> _registry;
        std::mutex _mtx;
    };


    class global_logger_builder : public logger_builder
    {
    public:
        virtual logger::ptr build() override
        {
            logger::ptr lgr = create();
            registry::instance().add(lgr);
            return lgr;
        }
    };
}

/*
 * 日志库接口设计
 *
 *
 *
 **/

namespace easylog
{

inline void add(const logger::ptr& logger)
{
    registry::instance().add(logger);
}
inline bool has(const std::string& name)
{
    return registry::instance().has(name);
}
inline logger::ptr get(const std::string& name)
{
    return registry::instance().get(name);
}
inline logger::ptr default_logger()
{
    return registry::instance().default_logger();
}

template<typename... Args>
inline void debug(logger::fmt_str_t fmt, Args&&... args)
{
    default_logger()->debug(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void info (logger::fmt_str_t fmt, Args&&... args)
{
    default_logger()->info (fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void warn (logger::fmt_str_t fmt, Args&&... args)
{
    default_logger()->warn (fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void error(logger::fmt_str_t fmt, Args&&... args)
{
    default_logger()->error(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void fatal(logger::fmt_str_t fmt, Args&&... args)
{
    default_logger()->fatal(fmt, std::forward<Args>(args)...);
}

#define EASYLOG_DEBUG(fmt, ...) easylog::debug(fmt, ##__VA_ARGS__)
#define EASYLOG_INFO( fmt, ...) easylog::info (fmt, ##__VA_ARGS__)
#define EASYLOG_WARN( fmt, ...) easylog::warn (fmt, ##__VA_ARGS__)
#define EASYLOG_ERROR(fmt, ...) easylog::error(fmt, ##__VA_ARGS__)
#define EASYLOG_FATAL(fmt, ...) easylog::fatal(fmt, ##__VA_ARGS__)

using level_enum = level::lv_enum;
using color_enum = util::color::color_enum;
using time_enum  = util::time::time_enum;
using size_enum  = util::size::size_enum;
using worker_type = logger_builder::work_type;
using logger_type = logger_builder::logger_type;

inline void set_level(level_enum lv)
{
    config::instance().set_level(lv);
}
inline void set_pattern(const std::string& p)
{
    config::instance().set_pattern(p);
}
inline void set_colorful(bool color)
{
    config::instance().set_colorful(color);
}
inline void set_color(const color_mode& cols)
{
    config::instance().set_color(cols);
}
inline void set_fn_pattern(const std::string& patt)
{
    config::instance().set_fn_pattern(patt);
}
inline void set_max_file_size(size_enum size)
{
    config::instance().set_max_file_size(size);
}
inline void set_max_live_time(time_enum time)
{
    config::instance().set_max_live_time(time);
}
inline void set_max_history(size_t num)
{
    config::instance().set_max_history(num);
}
inline void set_total_size_cap(size_enum size)
{
    config::instance().set_total_size_cap(size);
}
inline void set_work_type(worker_type type)
{
    config::instance().set_work_type(type);
}
inline void set_thread_num(size_t num)
{
    config::instance().set_thread_num(num);
}
inline void set_buffer_size(size_t size)
{
    config::instance().set_buffer_size(size);
}
inline void set_buffer_increment(size_t incr)
{
    config::instance().set_buffer_incr(incr);
}
inline void set_buffer_threshold(size_t thre)
{
    config::instance().set_buffer_thres(thre);
}

}
